const { reject } = require("lodash")

const PENDING = 'pengding'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
  // promise对象中传入的是一个执行器，执行resolve或reject
  constructor(executor) {
    executor(this.resolve, this.reject)
  }

  // 定义成功的值和失败的原因
  value = undefined
  reason = undefined
  // 定义状态
  status = PENDING
  // 定义异步时缓存的回调函数，由于一个promise可能调用多次所以定义成数组存放
  successCallback = []
  failCallback = []

  // resolve方法需要传入成功后的值,写成箭头函数使this可以访问MyPromise变量
  resolve = (value) => {
    // 当状态已经确定时不可改变，所以直接return
    if (this.status !== PENDING) return
    this.value = value
    this.status = FULFILLED
    // 异步的时候pending结束调用成功的回调，
    // 把成功回调数组里的函数都拿出来运行，判断回调数组里面是否还有值
    while(this.successCallback.length > 0) this.successCallback.shift()(this.value)
  }
  // reject方法需要传入失败的原因
  reject = (reason) => {
    if (this.status !== PENDING) return
    this.reason = reason
    this.status = REJECTED
    // 异步的时候pending结束调用失败的回调
    while(this.failCallback.length > 0) this.failCallback.shift()(this.reason)
  }
  // 传入then的成功和失败参数
  then(successCallback, failCallback) {
    // then传递空值时就值穿透
    successCallback = successCallback ? successCallback : value => value
    failCallback = failCallback ? failCallback : reason => reason

    // then方法实现链式调用，需要返回一个新的promise对象
    let promise2 = new MyPromise((resolve, reject) => {
      // 状态为成功的时候调用成功的方法, 并把成功后的值传入进去
      if (this.status === FULFILLED) {
        // 异步调用可以拿到promise2
        setTimeout(() => {
          let x = successCallback(this.value)
        // 根据x值的类型来进行不同的操作，普通值就直接resolve，如果是promise对象就要根据这个promise返回的结果来决定调用resolve或是reject
        // promise不能循环调用，所以要判断x和promise2是否相等
          resolvePromise(promise2, x, resolve, reject)
        }, 0);
        // return resolve(x)
      } else if (this.status === REJECTED) {
        // 状态为失败的时候调用失败的方法, 并把失败的原因传入进去
        // catch不存在链式调用，所以不需要return
        failCallback(this.reason)
      } else {
        // 异步情况，将回调存储起来
        this.successCallback.push(successCallback)
        this.failCallback.push(failCallback)
      }
    })
    return promise2
  }
}

// 定义判断x的方法
function resolvePromise (promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new Error('不能循环调用promise'))
  }
  if (x instanceof MyPromise) {
    x.then(value => resolve(value), reject => reject(value))
  } else {
    resolve(x)
  }
}
